package com.siebre.picclife.asynctask.service.support;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.google.common.collect.Maps;
import com.siebre.picclife.asynctask.TaskException;
import com.siebre.picclife.asynctask.model.Task;
import com.siebre.picclife.asynctask.service.TaskProcessor;
import com.siebre.picclife.asynctask.service.TaskService;

/**
 * 
 * @author ZhangChi
 * @since 2015年4月7日
 *
 */
public class MethodInvokingTaskProcessor implements TaskProcessor, TaskHandlerRegistry {
	
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	private final ArgumentDeserializerResolver deserializerResolver;
	
	private Map<String, TaskHandler> handlerMapping = Maps.newHashMap();

	private final TransactionTemplate transactionTemplate;
	
	private final TaskService taskService;

	public MethodInvokingTaskProcessor(
			ArgumentDeserializerResolver deserializerResolver,
			TransactionTemplate transactionTemplate,
			TaskService taskService) {
		this.deserializerResolver = deserializerResolver;
		this.transactionTemplate = transactionTemplate;
		this.taskService = taskService;
	}

	public void process(final Task task) {
		final TaskHandler handler = handlerMapping.get(task.getType());
		if (handler == null) {
			throw new TaskException("failed to find handler for task: " + task);
		}
		try {
			transactionTemplate.execute(new TransactionCallbackWithoutResult() {
				
				@Override
				protected void doInTransactionWithoutResult(TransactionStatus status) {
					handler.handle(getArguments(task, handler.getArgumentTypes()[0]));
				}
			});
			taskService.completeTask(task.getOid());
			logger.debug("complete task successfully, task={}", task);
		} catch (RuntimeException e) {
			logger.error("failed to process task: ", task, e);
			taskService.completeTaskWithFailure(task.getOid(), e);
		}
	}
	
	public void register(String type, TaskHandler handler) {
		handlerMapping.put(type, handler);
	}

	private Object[] getArguments(Task task, Class<?> argumentType) {
		ArgumentDeserializer argumentResolver = deserializerResolver.resolve(task.getType(), task.getArgument(), argumentType).orNull();
		if (argumentResolver == null) {
			throw new TaskException("failed to find argument deserializer for task " + task);
		}
		return new Object[] {argumentResolver.deserialize(task.getArgument(), argumentType)};
	}
}
